Skip to content

fix: filter temp-scoped state keys in SSE event generator#5203

Closed
surfai wants to merge 1 commit intogoogle:mainfrom
surfai:fix/sse-temp-state-serialization
Closed

fix: filter temp-scoped state keys in SSE event generator#5203
surfai wants to merge 1 commit intogoogle:mainfrom
surfai:fix/sse-temp-state-serialization

Conversation

@surfai
Copy link
Copy Markdown

@surfai surfai commented Apr 8, 2026

Summary

Fixes #5051

Temp state keys (prefix temp:) can contain non-serializable objects such as FunctionTool instances stored by _call_llm_node. _trim_temp_delta_state() already filters these for persistence in append_event(), but the SSE event generator serializes events before they reach that path, causing PydanticSerializationError when streaming=true.

This PR applies the same State.TEMP_PREFIX filtering in the SSE generator before model_dump_json().

Root cause

  1. _call_llm_node.py:131 stores FunctionTool objects in ctx.state['temp:tools_dict']
  2. State.delta is a reference to event_actions.state_delta — writes propagate immediately
  3. base_session_service.py:140 (_trim_temp_delta_state) filters temp: keys, but only inside append_event()
  4. adk_web_server.py:1918 serializes events via model_dump_json() before append_event()temp: keys with non-serializable values are still present → 💥

Non-streaming (/run) works because events pass through the full append_event() pipeline before serialization.

Changes

  • src/google/adk/cli/adk_web_server.py — Filter State.TEMP_PREFIX keys from state_delta in the SSE generator before model_dump_json() (mirrors _trim_temp_delta_state logic)
  • tests/unittests/cli/test_fast_api.py — Regression test: event with non-serializable temp: state keys streams successfully, non-temp keys are preserved

Test plan

  • New test test_agent_run_sse_filters_temp_state_keys verifies:
    • SSE response succeeds (no serialization crash)
    • Non-temp state keys (user_request) are preserved in the SSE output
    • Temp-scoped keys (temp:tools_dict, temp:other) are stripped
  • All existing SSE tests pass (8/8):
tests/unittests/cli/test_fast_api.py::test_agent_run_passes_state_delta PASSED
tests/unittests/cli/test_fast_api.py::test_agent_run_passes_invocation_id PASSED
tests/unittests/cli/test_fast_api.py::test_agent_run_sse_splits_artifact_delta PASSED
tests/unittests/cli/test_fast_api.py::test_agent_run_sse_does_not_split_artifact_delta_for_function_resume PASSED
tests/unittests/cli/test_fast_api.py::test_agent_run_sse_yields_error_object_on_exception PASSED
tests/unittests/cli/test_fast_api.py::test_agent_run_sse_filters_temp_state_keys PASSED
tests/unittests/cli/test_fast_api.py::test_auto_creates_session[/run_sse] PASSED
tests/unittests/cli/test_fast_api.py::test_returns_404_without_auto_create[/run_sse] PASSED

🤖 This PR was developed with AI assistance (Claude Code).

Temp state keys (prefix `temp:`) can contain non-serializable objects
such as `FunctionTool` instances stored by `_call_llm_node`.
`_trim_temp_delta_state()` already filters these for persistence in
`append_event()`, but the SSE event generator serializes events before
they reach that path, causing `PydanticSerializationError` when
`streaming=true`.

Apply the same `State.TEMP_PREFIX` filtering in the SSE generator
before `model_dump_json()`.

Fixes #5051
@adk-bot adk-bot added the live [Component] This issue is related to live, voice and video chat label Apr 8, 2026
@surfai surfai closed this by deleting the head repository Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

live [Component] This issue is related to live, voice and video chat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-serializable FunctionTool objects in state_delta cause PydanticSerializationError in AG-UI integration

2 participants